#include "tagfs.h"
#include "tools.h"
#include <cerrno>
#include <unistd.h>

// ----------------------------------------------------------------------------------------------------------
int TagFS::sptag__getattr(std::string tag, std::string name, struct stat* stbuf, struct fuse_file_info* info)
// ----------------------------------------------------------------------------------------------------------
{
	if (tag == "tags")
	{
		TagFile file = fsMaps.getFile(name);
		if (file.node == 0)
		{
			if (!fifo.empty()) fifo.pop();
			return -ENOENT;
		}

		// build tag string
		std::list<std::string>::iterator i;
		std::string tagString;
		std::list<std::string> list = fsMaps.getTags(file.name);

		for (i=list.begin(); i!=list.end(); ++i)
		{
			tagString += *i + "/";
		}
		if (!tagString.empty())
		{
			tagString = tagString.substr(0, tagString.size()-1);
		}
		tagString.append("\n");

		stbuf->st_mode = S_IFREG | 0664;
		stbuf->st_nlink = 0;
		stbuf->st_uid = getuid();
		stbuf->st_gid = getgid();
		stbuf->st_size = stbuf->st_blocks = tagString.size();
		stbuf->st_ctime = 0;
		stbuf->st_atime = 0;
		stbuf->st_mtime = 0;
		stbuf->st_blksize = 1;
		if (!fifo.empty()) fifo.pop();
		return 0;
	}
	else if (tag == "map")
	{
		stbuf->st_nlink = 0;
		stbuf->st_uid = getuid();
		stbuf->st_gid = getgid();
		stbuf->st_ctime = 0;
		stbuf->st_atime = 0;
		stbuf->st_mtime = 0;
		stbuf->st_blksize = 1;
		if (name == "dump")
		{
			stbuf->st_mode = S_IFREG | 0440;
			stbuf->st_size = stbuf->st_blocks = 0;
			if (!fifo.empty()) fifo.pop();
			return 0;
		}
		else if (name == "insert")
		{
			stbuf->st_mode = S_IFREG | 0220;
			stbuf->st_size = stbuf->st_blocks = 0;
			if (!fifo.empty()) fifo.pop();
			return 0;
		}
	}
	if (!fifo.empty()) fifo.pop();
	return 0;
}

// ----------------------------------------------------------------------------
int TagFS::sptag__open(std::string tag, std::string name, struct fuse_file_info* info)
// ----------------------------------------------------------------------------
{
	if (tag == "tags")
	{
		TagFile file = fsMaps.getFile(name);
		if (file.node == 0)
		{
			if (!fifo.empty()) fifo.pop();
			return -ENOENT;
		}
		if (info->flags & O_TRUNC)
		{
			sptag__truncate(tag, name, 0, info);
		}
		if (!fifo.empty()) fifo.pop();
		return 0;
	}
	else if (tag == "map")
	{
		if (name == "dump" && ! (info->flags & O_WRONLY || info->flags & O_RDWR) )
		{
			if (!fifo.empty()) fifo.pop();
			return 0;
		}
	}
	if (!fifo.empty()) fifo.pop();
	return -ENOENT;
}

// ------------------------------------------------------------------------------------------------------------------------------
int TagFS::sptag__read(std::string tag, std::string name, char* buf, uint64_t size, uint64_t offset, struct fuse_file_info* info)
// ------------------------------------------------------------------------------------------------------------------------------
{
	if (tag == "tags")
	{
		TagFile file = fsMaps.getFile(name);
		if (file.node == 0)
		{
			if (!fifo.empty()) fifo.pop();
			return -ENOENT;
		}

		// build tag string
		std::list<std::string>::iterator i;
		std::string tagString;
		std::list<std::string> list = fsMaps.getTags(file.name);

		for (i=list.begin(); i!=list.end(); ++i)
		{
			tagString += *i + "/";
		}
		if (!tagString.empty())
		{
			tagString = tagString.substr(0, tagString.size()-1);
		}
		tagString.append("\n");

		// return requested part
		if (offset >= tagString.size())
		{
			if (!fifo.empty()) fifo.pop();
			return -EINVAL;
		}
		if (offset+size > tagString.size())
		{
			size = tagString.size() - offset;
		}

		tagString = tagString.substr(offset, size);
		for (uint64_t i=0; i<size; ++i)
		{
			buf[i] = tagString[i];
		}
		if (!fifo.empty()) fifo.pop();
		return size;
	}
	else if (tag == "map")
	{
		if (name == "dump")
		{
			if (!fifo.empty()) fifo.pop();
			return 0;
		}
	}
	if (!fifo.empty()) fifo.pop();
	return 0;
}

// --------------------------------------------------------------------------------------------------------
int TagFS::sptag__truncate(std::string tag, std::string name, uint64_t length, struct fuse_file_info* info)
// --------------------------------------------------------------------------------------------------------
{
	if (tag == "tags")
	{
		TagFile file = fsMaps.getFile(name);
		if (file.node == 0)
		{
			if (!fifo.empty()) fifo.pop();
			return -ENOENT;
		}
		if (length == 0)
		{
			std::list<std::string> tags;
			std::list<std::string>::iterator i;
			tags = fsMaps.getTags(file.name);
			for (i=tags.begin(); i!=tags.end(); ++i)
			{
				fsMaps.rmvTag(*i, file.name);
			}
		}
		else
		{
			if (!fifo.empty()) fifo.pop();
			return -EINVAL;
		}
	}
	if (!fifo.empty()) fifo.pop();
	return 0;
}

// --------------------------------------------------------------------------------------------------------------------------------------
int TagFS::sptag__write(std::string tag, std::string name, const char* data, uint64_t size, uint64_t offset, struct fuse_file_info* info)
// --------------------------------------------------------------------------------------------------------------------------------------
{
	if (tag == "tags")
	{
		TagFile file = fsMaps.getFile(name);
		if (file.node == 0)
		{
			if (!fifo.empty()) fifo.pop();
			return -ENOENT;
		}

		// build tag string
		std::list<std::string>::iterator i;
		std::string tagString;
		std::list<std::string> list = fsMaps.getTags(file.name);

		for (i=list.begin(); i!=list.end(); ++i)
		{
			tagString += *i + "/";
		}
		if (!tagString.empty())
		{
			tagString = tagString.substr(0, tagString.size()-1);
		}
		tagString.append("\n");

		if (offset > tagString.size())
		{
			if (!fifo.empty()) fifo.pop();
			return -EINVAL;
		}

		// rewrite string
		
		uint64_t towr = tagString.size() - offset;
		uint64_t k;
		for (k=0; k < towr; ++k)
		{
			tagString[offset+k] = data[k];
		}
		towr = size - towr;

		if (towr > 0)
		{
			tagString.append(&data[k], towr);
		}
		
		if (tagString[tagString.size()-1] != 0x0A)
		{
			tagString.append("\n");
		}
		
		// delete old tags
		for (i=list.begin(); i!=list.end(); ++i)
		{
			fsMaps.rmvTag(*i, file.name);
		}

		// set new
		tagString = "/" + tagString.substr(0 ,tagString.size()-1) + "/";
		list.clear();
		tools::splitPath(&list, tagString);
		for (i=list.begin(); i!=list.end(); ++i)
		{
			fsMaps.addTag(*i, file.name);
		}
		
		if (!fifo.empty()) fifo.pop();
		return size;
	}
	if (!fifo.empty()) fifo.pop();
	return 0;
}

// --------------------------------------------------------
int TagFS::sptag__unlink(std::string tag, std::string name)
// --------------------------------------------------------
{
	if (!fifo.empty()) fifo.pop();
	return -EPERM;
}

// --------------------------------------------------------------------------------------
int TagFS::sptag__utimens(std::string tag, std::string name, const struct timespec tv[2])
// --------------------------------------------------------------------------------------
{
	if (!fifo.empty()) fifo.pop();
	return -EPERM;
}

// ------------------------------------------------------------------
int TagFS::sptag__access(std::string tag, std::string name, int mode)
// ------------------------------------------------------------------
{
	if (tag == "tags")
	{
		TagFile file = fsMaps.getFile(name);
		if (mode & (R_OK | W_OK | F_OK))
		{
			if (file.node == 0)
			{
				if (!fifo.empty()) fifo.pop();
				return -ENOENT;
			}
			if (!fifo.empty()) fifo.pop();
			return 0;
		}
		if (!fifo.empty()) fifo.pop();
		return -EACCES;
	}
	if (tag == "map")
	{
		if (name == "dump")
		{
			if (mode & (R_OK | F_OK))
			{
				if (!fifo.empty()) fifo.pop();
				return 0;
			}
			if (!fifo.empty()) fifo.pop();
			return -EACCES;
		}
		else if (name == "insert")
		{
			if (mode & (W_OK | F_OK))
			{
				if (!fifo.empty()) fifo.pop();
				return 0;
			}
			if (!fifo.empty()) fifo.pop();
			return -EACCES;
		}
	}
	if (!fifo.empty()) fifo.pop();
	return -EACCES;
}

/*

// ---------------
void TagFS::info()
// ---------------
{
	printf("Filesystem size: %llu bytes\n", fsSize-4);
	printf("Map space: %llu/%llu bytes\n", mapsSize, mapsSizeMax);
	printf("Data block size: %u bytes\n", blockSize);
	printf("Data blocks: %llu/%llu\n", ((fsSize-mapsSizeMax-10)/blockSize)-freeBlocks, ((fsSize-mapsSizeMax-10)/blockSize));
	printf("Wasted bytes: %llu\n", (fsSize-mapsSizeMax-10) % blockSize);
}

// ---------------------
void TagFS::map__print()
// ---------------------
{
	std::map<uint64_t, file>::iterator filei;
	std::list<filePos>::iterator fileposi;
	std::map<std::string, std::list<uint64_t> >::iterator tagi;
	std::map<std::string, uint64_t>::iterator rfilei;
	std::list<uint64_t>::iterator li;

	for (filei=fileMap.begin(); filei!=fileMap.end(); ++filei)
	{
		printf("[fileMap] %u => %s ", (uint32_t)filei->first, filei->second.name.data());
		for (fileposi=filei->second.blocks.begin(); fileposi!=filei->second.blocks.end(); ++fileposi)
		{
			printf("%u+%u ", (uint32_t)fileposi->start, (uint32_t)fileposi->length);
		}
		printf("\n");
	}
	printf("[usedBlocks] ");
	for (fileposi=usedBlocks.begin(); fileposi!=usedBlocks.end(); ++fileposi)
	{
		printf("%u+%u ", (uint32_t)fileposi->start, (uint32_t)fileposi->length);
	}
	printf("\n");
	for (rfilei=reverseFileMap.begin(); rfilei!=reverseFileMap.end(); ++rfilei)
	{
		printf("[reverseFileMap] %s => %u\n", rfilei->first.data(), (uint32_t)rfilei->second);
	}
	for (tagi=tagMap.begin(); tagi!=tagMap.end(); ++tagi)
	{
		printf("[tagMap] %s => ", tagi->first.data());
		for (li=tagi->second.begin(); li!=tagi->second.end(); ++li)
		{
			printf("%u ", (uint32_t)*li);
		}
		printf("\n");
	}
}

*/
